﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace Nirvana
{
    /// <summary>
    /// 创建使用各种变体的材质球，使所有模型的mat都独立，不在shader效果下相互影响
    /// </summary>
    public class RendererMaterialCache : Singleton<RendererMaterialCache>
    {
        
        private struct RendererMaterialCacheData
        {
            public int instanceid;
            public int queue;
            public ShaderKeywords shaderKeywords;
        }
        private class RendererMaterialCacheCompare : IEqualityComparer<RendererMaterialCacheData>
        {

            public bool Equals(RendererMaterialCacheData src, RendererMaterialCacheData dst)
            {
                return src.instanceid == dst.instanceid && src.queue == dst.queue && src.shaderKeywords.Equals(dst.shaderKeywords);
            }
            public int GetHashCode(RendererMaterialCacheData data)
            {
                int num = data.instanceid.GetHashCode();
                num = (397 * num ^ data.queue.GetHashCode());
                return 397 * num ^ data.shaderKeywords.GetHashCode();
            }
        }
        private Dictionary<RendererMaterialCacheData, WeakReference> rendercachemap = new Dictionary<RendererMaterialCacheData, WeakReference>(new RendererMaterialCacheCompare());

        public RendererMaterialCache()
        {
#if UNITY_EDITOR
            EditorApplication.playModeStateChanged += (p)=> {
                if (!EditorApplication.isPaused)
                {
                    this.rendercachemap.Clear();
                }
            };
#endif

        }

        public Material CreateMaterial(Material mat,int queue, ShaderKeywords keys)
        {
            RendererMaterialCacheData key;
            key.instanceid = mat.GetInstanceID();
            key.queue = queue;
            key.shaderKeywords = keys;
            WeakReference weakReference;

            if (this.rendercachemap.TryGetValue(key, out weakReference))
            {
                if (weakReference.IsAlive)
                {
                    return (Material)weakReference.Target;
                }
                this.rendercachemap.Remove(key);
            }
            Material material = CreateNewMaterial(mat, queue, keys);
            this.rendercachemap.Add(key, new WeakReference(material));
            return material;
        }
        private static Material CreateNewMaterial(Material mat, int queue, ShaderKeywords keys)
        {
            Material material = new Material(mat);
            material.hideFlags = HideFlags.DontSave;
            if (queue != -1)
            {
                material.renderQueue = queue;
            }
            foreach (int keyword in keys)
            {
                string keywordName = ShaderKeywords.GetKeywordName(keyword);
                material.EnableKeyword(keywordName);
                Material material2 = material;
                material2.name = material2.name + "[" + keywordName + "]";
            }
            return material;
        }
#if UNITY_EDITOR

        [MenuItem("Nirvana/Render/Clear Material Cache")]
        public static void ClearCache()
        {
            foreach(var keyValuePair in Singleton<RendererMaterialCache>.Instance.rendercachemap)
            {
                if (Application.isPlaying)
                {
                    WeakReference value = keyValuePair.Value;
                    if (value.IsAlive)
                    {
                        Material material = (Material)value.Target;
                        UnityEngine.Object.Destroy(material);
                    }
                }
                else
                {
                    WeakReference value2 = keyValuePair.Value;
                    if (value2.IsAlive)
                    {
                        Material material2 = (Material)value2.Target;
                        UnityEngine.Object.DestroyImmediate(material2);
                    }
                }
            }

            Singleton<RendererMaterialCache>.Instance.rendercachemap.Clear();
            SceneView.RepaintAll();
        }
#endif


    }
}

